/*
 * Created by Sam Sune on 2020/10/21.
 *
 * Copyright (c) 2020 belonk.com All rights reserved.
 */
#include <stdio.h>

/*
二进制计算规则：
1) 二进制的最高位是符号位: 0 表示正数,1 表示负数
2) 正数的原码，反码，补码都一样 (三码合一)
3) 负数的反码=它的原码符号位不变，其它位取反(0->1,1->0)
4) 负数的补码=它的反码+1	//
5) 0的反码，补码都是0
6) 在计算机运算的时候，都是以补码的方式来运算的
*/
void fun1() {
    // int在不同的机器可能占2到4个字节，先查看一下
    printf("int占用字节数: %ld", sizeof(int)); // 输出：int占用字节数: 4
    /*
    题目：对整数2取反，取反：1变0，0变1.
    分析：int在我的机器占4个字节，每个字节8bit(位)，共32位，计算都按照补码运算的，所以需要先取得2的补码，然后取反，最后将补码转为原码即可。
     取反步骤如下：
     1、计算2的二进制，表示为4个字节，即32bit: 00000000 00000000 00000000 00000010
     2、求得2的补码，正数的补码、反码都是其本身，所以2的补码为：00000000 00000000 00000000 00000010
     3、补码开始位运算，~表示按位取反，得：11111111 11111111 11111111 11111101
     4、得到的结果是补码，高位为1，是负数，要转换为原码需要先转换为反码，即减去1记得到反码：11111111 11111111 11111111 11111100
     5、然后将反码转换为原码（符号位不变，其他为取反）：10000000 00000000 00000000 00000011
     6、计算原码的值，结果为：-3，故~2的结果为-3
     */
    int a = ~2;
    printf("\n ~2 = %d", a);
}

void fun2() {
    /*
    题目：对整数-2取反，取反：1变0，0变1.
    分析：int在我的机器占4个字节，每个字节8bit(位)，共32位，计算都按照补码运算的，所以需要先取得-2的补码，然后取反，最后将补码转为原码即可。
     取反步骤如下：
     1、计算-2的二进制，表示为4个字节，即32bit: 00000000 00000000 00000000 00000010
     2、求得-2的反码，符号位不变，其他位取反，得到：11111111 11111111 11111111 11111101
        负数的补码为其反码+1，所以-2的补码为：11111111 11111111 11111111 11111110
     3、补码开始位运算，~表示按位取反，得：00000000 00000000 00000000 00000001
     4、得到的结果是补码，要转换为原码需要先转换为反码，高位为0，则为正数，正数的反码、补码都是其原码，即其原码为：00000000 00000000 00000000 00000001
     5、原码的值表示为1，故~(-2)的值为1
     */
    int a = ~(-2);
    printf("\n ~(-2) = %d", a);
}

void fun3() {
    /*
    原码：10000000 00000000 00000000 00001010
    反码：11111111 11111111 11111111 11110101
    补码：11111111 11111111 11111111 11110110
    取反：00000000 00000000 00000000 00001001 结果为补码
    补码转原码：
     正数，原码、反码、补码相同：00000000 00000000 00000000 00001001
     结果为9

     */
    int a = ~(-10); // 9
    printf("\n ~(-10) = %d", a);
}

void fun4() {
    /*
     * 规则：
     * 1)算术右移 >>：低位溢出,符号位不变,并用符号位补溢出的高位
       2)算术左移 <<:	符号位不变，低位补 0
     * 规律：
     * 1、正数、负数左移一位即是乘以2
     * 2、正数右移一位即是除以2
     * 3、负数右移不能按照上述规则
     */
    int a = 1 >> 2; // = 1 / 2 / 2 = 0, 1向右位移2位 ,  这里还涉及到二进制中原码，反码，补码
    /*
     * -1:
     * 原码： 10000000 00000000 00000000 00000001
     * 反码： 11111111 11111111 11111111 11111110
     * 补码： 11111111 11111111 11111111 11111111
     * 补码右移2位，高位补符号位，低位舍弃，得：11111111 11111111 11111111 11111111
     * 结果为补码，转换为原码：
     *   反码：11111111 11111111 11111111 11111110
     *   原码：10000000 00000000 00000000 00000001
     *   结果-1
     */
    int b = -1 >> 2; // -1
    /*
     * 原码：00000000 00000000 00000000 00000001
     * 补码：00000000 00000000 00000000 00000001
     * 补码左移两位，符号位不变，低位补0，为：00000000 00000000 00000000 00000100
     * 结果为补码，且是正数，所以原码与补码一致，结果为：4
     */
    int c = 1 << 2; // 4
    /*
     * 原码： 10000000 00000000 00000000 00000001
     * 反码： 11111111 11111111 11111111 11111110
     * 补码： 11111111 11111111 11111111 11111111
     * 补码左移两位，得到：11111111 11111111 11111111 11111100
     * 补码-1得到反码：11111111 11111111 11111111 11111011
     * 转为原码：10000000 00000000 00000000 00000100
     * 结果为-4
     */
    int d = -1 << 2;// -4
    // a,b,c,d,e 结果是多少
    printf("\n a=%d b=%d c=%d d=%d ", a, b, c, d);
    printf("\n -2 << 2 = %d", -2 << 2);
    printf("\n -3 << 3 = %d", -3 << 3);
    printf("\n -2 >> 2 = %d", -2 >> 2);
    printf("\n -3 >> 2 = %d", -3 >> 2);
    printf("\n -60 >> 2 = %d", -60 >> 2);
}

int main() {
    fun1();
    printf("\n ----------");
    fun2();
    printf("\n ----------");
    fun3();
    printf("\n ----------");
    fun4();
}
